﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.DLR;

namespace Workflows.Components.TextMining.Collapse
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    [OrmClass("Entity", "EntityID", true)]
    public class CollapsibleEntitySettings
    {
        #region fields
        private string _ConnStr;
        private string _KeyFieldName;
        private string _TableName;
        private string _CollapsedKeyFieldName;
        private string _CollapsedTableName;
        private CollapsibleObjectAttribute _ClassAttr;
        private Dictionary<string, EvaluableFieldAttribute> _FieldAttrs;
        private Type _EntityType;
        #endregion

        #region ctor
        private CollapsibleEntitySettings(
            string connStr, string keyFieldName, 
            string tblName, string collapsedTblName, 
            string collapsedKeyFieldName)
        {
            this._ConnStr = connStr;
            this._KeyFieldName = keyFieldName;
            this._TableName = tblName;
            this._CollapsedKeyFieldName=collapsedKeyFieldName;
            this._CollapsedTableName = collapsedTblName;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connStr"></param>
        /// <param name="keyFieldName"></param>
        /// <param name="tblName"></param>
        /// <param name="collapsedTblName"></param>
        /// <param name="collapsedKeyFieldName"></param>
        /// <param name="entityTypeName"></param>
        public CollapsibleEntitySettings(
            string connStr, string keyFieldName,
            string tblName, string collapsedTblName,
            string collapsedKeyFieldName, string entityTypeName)
            :this(connStr,keyFieldName,tblName,collapsedTblName,collapsedKeyFieldName)
        {
            this._EntityType = this.GetType().Assembly.GetType(entityTypeName);
            if (this._EntityType != null)
            {
                this.ClassAttr = this.GetClassAttributeForType(this._EntityType);
                this._FieldAttrs = this.PopulateEvaluableFieldsForType(this._EntityType);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connStr"></param>
        /// <param name="keyFieldName"></param>
        /// <param name="tblName"></param>
        /// <param name="collapsedTblName"></param>
        /// <param name="collapsedKeyFieldName"></param>
        /// <param name="entityType"></param>
        public CollapsibleEntitySettings(
            string connStr, string keyFieldName,
            string tblName, string collapsedTblName,
            string collapsedKeyFieldName, Type entityType)
            : this(connStr, keyFieldName, tblName, collapsedTblName, collapsedKeyFieldName)
        {
            this._EntityType = entityType;
            this.ClassAttr = this.GetClassAttributeForType(this._EntityType);
            this._FieldAttrs = this.PopulateEvaluableFieldsForType(this._EntityType);
        }

        /// <summary>
        /// load settings from xml config file
        /// </summary>
        /// <param name="xDoc"></param>
        public CollapsibleEntitySettings(XmlDocument xDoc)
        {
            XmlNode root = xDoc.DocumentElement;
            this._ConnStr = XmlDataUtil.GetAttributeValue(root, "ConnectionString", "");
            this._TableName = XmlDataUtil.GetAttributeValue(root, "TableName", "");
            this._KeyFieldName = XmlDataUtil.GetAttributeValue(root, "KeyFieldName", "");
            this._CollapsedTableName = XmlDataUtil.GetAttributeValue(root, "CollapsedTableName", "");
            this._CollapsedKeyFieldName = XmlDataUtil.GetAttributeValue(root, "CollapsedKeyFieldName", "");

            string entityTypeName = XmlDataUtil.GetAttributeValue(root, "TypeName", "");
            this._EntityType = this.GetType().Assembly.GetType(entityTypeName);
            string keyName = XmlDataUtil.GetAttributeValue(root, "KeyFieldName", "");
            double cutoffScore = double.Parse(XmlDataUtil.GetAttributeValue(root, "CutoffScore", "0"));
            bool maximizeFieldScore =
                bool.Parse(XmlDataUtil.GetAttributeValue(root, "MaximizeFieldScore", true.ToString()));
            this._ClassAttr = new CollapsibleObjectAttribute(this._EntityType, keyName, cutoffScore, maximizeFieldScore);
            XmlNodeList fieldEvalNodes = root.SelectNodes("//EvaluableField");
            this._FieldAttrs=new Dictionary<string, EvaluableFieldAttribute>();
            if(fieldEvalNodes !=null && fieldEvalNodes.Count>0)
            {
                foreach(XmlNode fieldEvalNode in fieldEvalNodes)
                {
                    string memberName = XmlDataUtil.GetAttributeValue(fieldEvalNode, "MemberName", "");
                    FieldType fieldType =
                        (FieldType)
                        int.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "FieldType",
                                                                ((int) FieldType.Character).ToString()));
                    ValueEvaluateMethods evalMethod =
                        (ValueEvaluateMethods)
                        int.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "EvalMethod",
                                                                ((int) ValueEvaluateMethods.Equals).ToString()));
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "Weight", "0"));
                    double fromVal =
                        double.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "FromValue",
                                                                   double.MinValue.ToString()));
                    double toVal =
                        double.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "ToValue", double.MaxValue.ToString()));
                    int minLen = int.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "MinLen", "0"));
                    int maxLen =
                        int.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "MaxLen", int.MaxValue.ToString()));
                    bool nullible =
                        bool.Parse(XmlDataUtil.GetAttributeValue(fieldEvalNode, "AllowNull", false.ToString()));
                    EvaluableFieldAttribute fieldEvaluable=new EvaluableFieldAttribute(memberName,fieldType);
                    fieldEvaluable.EvaluateMethod = evalMethod;
                    fieldEvaluable.Weight = weight;
                    fieldEvaluable.FromValue = fromVal;
                    fieldEvaluable.ToValue = toVal;
                    fieldEvaluable.MinLen = minLen;
                    fieldEvaluable.MaxLen = maxLen;
                    fieldEvaluable.AllowNull = nullible;
                    this._FieldAttrs.Add(memberName,fieldEvaluable);
                }
            }
        }
        #endregion

        public Dictionary<string, EvaluableFieldAttribute> FieldAttrs
        {
            get { return _FieldAttrs; }
            set { _FieldAttrs = value; }
        }

        public string EntityTypeName
        {
            get { return _EntityType.Name; }
        }

        public CollapsibleObjectAttribute ClassAttr
        {
            get { return _ClassAttr; }
            set { _ClassAttr = value; }
        }

        public string ConnStr
        {
            get { return _ConnStr; }
            set { _ConnStr = value; }
        }

        public string KeyFieldName
        {
            get { return _KeyFieldName; }
            set { _KeyFieldName = value; }
        }

        public string CollapsedKeyFieldName
        {
            get { return _CollapsedKeyFieldName; }
            set { _CollapsedKeyFieldName = value; }
        }

        public string TableName
        {
            get { return _TableName; }
            set { _TableName = value; }
        }

        public string CollapsedTableName
        {
            get { return _CollapsedTableName; }
            set { _CollapsedTableName = value; }
        }

        public XmlDocument SaveAsXml()
        {
            XmlDocument xDoc=new XmlDocument();
            xDoc.LoadXml("<CollapsibleEntitySettings></CollapsibleEntitySettings>");
            XmlNode root = xDoc.DocumentElement;
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"ConnectionString",this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"TableName",this.TableName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"KeyFieldName",this.KeyFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"CollapsedTableName",this.CollapsedTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"CollapsedKeyFieldName",this.CollapsedKeyFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"TypeName",this.EntityTypeName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"KeyFieldName",this.ClassAttr.KeyName);
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"CutoffScore",this.ClassAttr.CollapseCutoffScore.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc,root,"MaximizeFieldScore",true.ToString());
            if(this.FieldAttrs !=null && this.FieldAttrs.Count>0)
            {
                foreach(string memberName in this.FieldAttrs.Keys)
                {
                    EvaluableFieldAttribute fieldEval = this.FieldAttrs[memberName];
                    XmlNode fieldNode = XmlDataUtil.AddElement(ref xDoc, root, "EvaluableField");
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"MemberName",memberName);
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"FieldType",((int)fieldEval.FieldType).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"EvalMethod",((int)fieldEval.EvaluateMethod).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"Weight",fieldEval.Weight.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"FromValue",fieldEval.FromValue.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"ToValue",fieldEval.ToValue.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"MinLen",fieldEval.MinLen.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"MaxLen",fieldEval.MaxLen.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc,fieldNode,"AllowNull",fieldEval.AllowNull.ToString());
                }
            }
            return xDoc;
        }

        private CollapsibleObjectAttribute GetClassAttributeForType(Type type)
        {
            object[] customClassAtts = type.GetCustomAttributes(typeof (CollapsibleObjectAttribute), false);
            if(customClassAtts !=null && customClassAtts.Length==1)
            {
                CollapsibleObjectAttribute objAtt = (CollapsibleObjectAttribute) customClassAtts[0];
                return objAtt;
            }
            else
            {
                return null;
            }
        }

        private Dictionary<string, EvaluableFieldAttribute> PopulateEvaluableFieldsForType(Type type)
        {
            MemberInfo[] members = type.GetMembers();
            Dictionary<string,EvaluableFieldAttribute> fieldAtts=new Dictionary<string, EvaluableFieldAttribute>();
            if(members !=null && members.Length>0)
            {
                foreach(MemberInfo member in members)
                {
                    if(member.MemberType==MemberTypes.Field)
                    {
                        object[] atts = ((FieldInfo) member).GetCustomAttributes(typeof (EvaluableFieldAttribute), false);
                        if(atts !=null && atts.Length==1)
                        {
                            EvaluableFieldAttribute att = (EvaluableFieldAttribute) atts[0];
                            fieldAtts.Add(member.Name,att);
                        }
                    }
                    else if(member.MemberType==MemberTypes.Property)
                    {
                        object[] atts = ((PropertyInfo) member).GetCustomAttributes(typeof (EvaluableFieldAttribute),
                                                                                    false);
                        if(atts != null && atts.Length==1)
                        {
                            EvaluableFieldAttribute att = (EvaluableFieldAttribute) atts[0];
                            fieldAtts.Add(member.Name,att);
                        }
                    }
                }
            }
            return fieldAtts;

        }

        public object GetEntityByKey(int id)
        {
            return OrmUtil.GetObjectByKey(this._ConnStr, this._EntityType, id.ToString());
        }

        public CollapsedEntity GetCollapsedEntityByKey(int collapsedId)
        {
            return
                (CollapsedEntity)
                OrmUtil.GetObjectByKey(this._ConnStr, typeof (CollapsedEntity), collapsedId.ToString());
        }
    }
}
